#pragma once

#include <iostream>
#include <string>
#include <unordered_map>
#include <sstream>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <fcntl.h>
#include <fstream>

using namespace std;

string prefix("./wwwroot");             // web根目录
string defaultfile("index.html");       // 默认文件
string errorfile("./wwwroot/404.html"); // 404文件

// 请求
class request
{
public:
    // 分离报头名称与内容
    void detach(const string &header, string &name, string &value)
    {
        auto pos = header.find(": ");
        if (pos == string::npos)
            return;

        name = header.substr(0, pos);
        value = header.substr(pos + 2);
    }

    // 反序列化
    void deserialize(const string &reqstr)
    {
        // 读取请求行
        auto first = reqstr.find("\r\n");
        if (first == string::npos)
            return;

        _reqline = reqstr.substr(0, first); // 不包含""\r\n"
        // 分离请求行
        stringstream in(_reqline);
        in >> _method >> _url >> _version;

        // 获取请求资源的完整路径
        _path = prefix;
        _path += _url;
        if (_url == "/")
            _path += defaultfile;

        cout << _path << endl;

        // 读取请求报头
        string header;
        auto second = reqstr.find("\r\n", first + 2);
        while (second != first + 2)
        {
            if (second == string::npos)
                return;

            header = reqstr.substr(first + 2, second - first - 2);
            string name, value;
            // 分离属性名称与内容
            detach(header, name, value);
            _reqHeaders[name] = value; // 存储报头

            first = second;
            second = reqstr.find("\r\n", first + 2);
        }

        // 读取请求报文
        _reqbody = reqstr.substr(second + 2);
    }

public:
    string _reqline;                           // 请求行
    unordered_map<string, string> _reqHeaders; // 请求报头
    string _reqbody;                           // 请求正文
    string _method;                            // 请求方式
    string _url;                               // 请求的资源
    string _version;                           // 版本类型
    string _path;                              // url文件完整路径
};

// 响应
class response
{
public:
    // 对响应进行序列化
    void serialize(string &respstr)
    {
        respstr.clear();
        // 添加响应行
        respstr += _respline;
        respstr += "\r\n";
        // 添加响应报头
        for (auto &kv : _respHeaders)
        {
            respstr += kv.first;
            respstr += ": ";
            respstr += kv.second;
            respstr += "\r\n";
        }
        // 添加空行
        respstr += "\r\n";

        // 添加响应正文
        respstr += _respbody;
    }

public:
    string _respline;                           // 响应行
    unordered_map<string, string> _respHeaders; // 响应报头
    string _respbody;                           // 响应正文
};

// 获取资源类型
string getfiletype(string path)
{
    auto pos = path.rfind('.');
    string type = path.substr(pos);
    if (type == ".html")
        return "text/html";
    else if (type == ".jpg")
        return "application/x-jpg";
    else if (type == ".img")
        return "application/x-img";
    else
        return "";
}

bool readfile(const char *path, string &body)
{
    // 1、先获取文件状态，包括文件大小
    struct stat st;
    if (stat(path, &st) != 0)
        return false;

    // 2、获取文件状态成功，以二进制读取形式打开文件
    ifstream in(path, ifstream::binary);
    if (!in.is_open())
        return false;

    // 3、打开文件成功，读取数据
    body.resize(st.st_size);                   // 提前给正文扩容
    in.read((char *)body.c_str(), st.st_size); // st.st_size为文件大小,读取整个文件

    return true;
}

// 根据请求生成响应
bool getResponse(const request &req, response &resp)
{
    // 初始化响应行
    resp._respline = "HTTP/1.1 200 OK";

    // 打开path对应的资源文件，以二进制的方式读取其中的资源，并存储到响应正文中
    if (!readfile(req._path.c_str(), resp._respbody))
        readfile(errorfile.c_str(), resp._respbody);

    // 获取资源类型
    string filetype = getfiletype(req._path);

    // 添加响应报头
    resp._respHeaders["Content-Type"] = filetype;
    resp._respHeaders["Content-Length"] = to_string(resp._respbody.size());

    return true;
}